/*
 * @(#)ExecuteInfo.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.nhncorp.usf.core.config.runtime.Assign;
import com.nhncorp.usf.core.config.runtime.BusinessObject;
import com.nhncorp.usf.core.config.runtime.BusinessObjectGroup;
import com.nhncorp.usf.core.config.runtime.ResultPageInfo;
import com.nhncorp.usf.core.config.runtime.ScriptInfo;
import com.nhncorp.usf.core.config.runtime.Service;
import com.nhncorp.usf.core.config.runtime.ServiceCondition;
import com.nhncorp.usf.core.service.ServiceExecutionContext.MethodType;
import com.nhncorp.usf.core.service.ServiceType;

/**
 * DynamicConfigTester 로 DynamicConfig 파일을 실행한 결과
 *
 * @author Web Platform Development Team
 */
public class ExecuteInfo {
	private List<String> sequentialExecutedMethods = new ArrayList<String>();
	private List<String> parallelExecutedMethods = new ArrayList<String>();
	private StringBuilder conditionBuffer = new StringBuilder();
	private Map<String, String> executionInfo = new HashMap<String, String>();
	private List<Service> executionPath = new ArrayList<Service>();
	private ResultPageInfo resultPageInfo;
	private Map<String, Object> dataModel;
	
	/**
	 * @return 순차 실행한 Method 목록 반환 (package.Class.method 방식으로 기술됨) 
	 * 
	 */
	public List<String> getSequentialExecutedMethods() {
		return sequentialExecutedMethods;
	}

	/**
	 * 실행 Method 를 추가 
	 * 
	 * @param method package.Class.method 방식으로 기술 됨
	 * @param methodType Sequential, Parallel 구분
	 * */
	public void addExecuteMethod(String method, MethodType methodType) {
		if (methodType == MethodType.sequential) {
			sequentialExecutedMethods.add(method);
		} else if (methodType == MethodType.parallel) {
			parallelExecutedMethods.add(method);
		}
		
		executionInfo.put(method, getCurrentExecutionPath());
	}

	/**
	 * 현재 executionPath 를 반환한다.
	 * 
	 * @return 현재 executionPath 
	 * */
	private String getCurrentExecutionPath() {
		conditionBuffer.setLength(0);
		
		for (Service service : executionPath) {
			String path = getServicePath(service);
			conditionBuffer.append("/").append(path);
		}
		
		return conditionBuffer.toString();
	}

	/**
	 * service 의 path 를 표시할 String 으로 반환한다.
	 * 
	 * @param service 출력할 {@link Service}
	 * @return service 의 path 출력할 String 문자열 
	 * */
	private String getServicePath(Service service) {
		if (service.getServiceType() == ServiceType.conditionBlock) {
			String condition = ((ServiceCondition)service).getCondition();
			return service.getNodeName() + "(" + condition + ")";
		} else if (service.getServiceType() == ServiceType.bo) {
			String boId = ((BusinessObject)service).getId();
			return service.getNodeName() + "(" + boId + ")";
		} else if (service.getServiceType() == ServiceType.condition_script) {
			String function = ((ScriptInfo)service).getFunctionName();
			return service.getNodeName() + "(" + function + ")";
		} else if (service.getServiceType() == ServiceType.bo_group) {
			String function = ((BusinessObjectGroup)service).getScriptInfo().getFunctionName();
			return service.getNodeName() + "(" + function + ")";
		} else if (service.getServiceType() == ServiceType.assign) {
			String script = ((Assign) service).getScript();
			return service.getNodeName() + "(" + script + ")";
		} else {
			return service.getNodeName();
		}
	}

	/**
	 * 실행 Method 의 실행 경로를 반환한다. 단순 method 명으로만 비교하므로 2개 이상일 경우 마지막 1개만 반환한다.<br/>
	 * 테스트할 때 method 명을 다르게 해주는 센스가 필요하다. 
	 * 
	 * @param method 실행 경로를 확인할 method. (package.Class.method 방식으로 기술됨)
	 * @return method 의 실행 경로
	 * */
	public String getExecutionInfo(String method) {
		return executionInfo.get(method);
	}

	/**
	 * @return 병렬 실행한  Method 목록 반환 (package.Class.method 방식으로 기술됨)
	 * */
	public List<String> getParallelExecutedMethods() {
		return parallelExecutedMethods;
	}

	/**
	 * 실행 Path 로 service 를 추가한다.
	 * 
	 * @param service 실행 경로에 추가할 {@link Service}
	 * */
	public void addExecutionPath(Service service) {
		pop(service);
		executionPath.add(service);
	}

	/**
	 * executionPath 에서 service 의 parent 가 나올 때 까지 pop 한다.
	 * 
	 * @param service service 의 parent 가 stack 에 나올 때까지 pop 한다.
	 * */
	private void pop(Service service) {
//		condition-script 는 if/when 등의 하위 요소지만 if/when 보다 먼저 계산되기 때문에 parent 가 없는 상황에서 add 된다.
		if (service.getServiceType() == ServiceType.condition_script) {
			return;
		}
		
		Service parent = service.getParent();
		
		for (int i = executionPath.size() - 1; i >= 0; i--) {
			Service tempService = executionPath.get(i);
			
			if (tempService == parent) {
				break;
			} else {
				// condition-script 는 if/when 등의 하위 요소지만 if/when 보다 먼저 계산되기 때문에 parent 가 없는 상황에서 add 된다.
				if (tempService.getServiceType() != ServiceType.condition_script) {
					executionPath.remove(i);
				}
			}
		}
	}

	/**
	 * @return run.xml 실행 결과로 선택된 최종 출력 page 반환
	 * */
	public ResultPageInfo getResultPageInfo() {
		return resultPageInfo;
	}

	/**
	 * @param resultPageInfo ResultPage 정보
	 * 
	 */
	public void setResultPageInfo(ResultPageInfo resultPageInfo) {
		this.resultPageInfo = resultPageInfo;
	}

	public void setDataModel(Map<String, Object> dataModel) {
		this.dataModel = dataModel;
	}
	
	/**
	 * @return DataModel
	 * */
	public Map<String, Object> getDataModel() {
		if (dataModel == null) {
			return Collections.emptyMap();
		} else {
			return dataModel;
		}
	}
}
